package server

import (
	"errors"
	"reflect"
	"sieve_admin_server_userpass/pkg/config"
	"sieve_admin_server_userpass/pkg/log"
	"sort"
	"strings"
)

var (
	mods    = map[string]*Mod{}
	modList = []*Mod{}
	cfgMods = map[string]*Mod{}
)

func init() {
	config.Sequence("mod", "模块", func(cfg *config.SeqCfg) {
		mod := &Mod{
			Name:     *cfg.String("name", "", "mod name"),
			Title:    *cfg.String("title", "", "mod title"),
			TagTitle: *cfg.String("tagTitle", "", "mod tagTitle"),
			Parent:   *cfg.String("parent", "", "mod parent"),
			Order:    *cfg.Int("order", 0, "mod order"),
			Perms:    []*Perm{},
			Icon:     *cfg.String("icon", "", "mod icon"),
		}
		cfg.Sequence("perm", "权限", func(cfg *config.SeqCfg) {
			perm := &Perm{
				Name:  *cfg.String("name", "", "perm name"),
				Title: *cfg.String("title", "", "perm title"),
			}
			mod.Perms = append(mod.Perms, perm)
		})
		if _, ok := cfgMods[mod.Name]; ok {
			log.Fatal("duplicate mod " + mod.Name)
		}
		cfgMods[mod.Name] = mod
	})
}

func RegMod(mod *Mod) {
	if _, ok := mods[mod.Name]; ok {
		log.Fatal("duplicate mod " + mod.Name)
	}
	pmap := map[string]bool{}
	for _, p := range mod.Perms {
		pmap[p.Name] = true
	}

	mod.apis = map[string]func(*Context) interface{}{}
	if mod.Obj != nil {
		t := reflect.TypeOf(mod.Obj)
		v := reflect.ValueOf(mod.Obj)
		for i := 0; i < t.NumMethod(); i++ {
			mname := t.Method(i).Name
			if strings.HasPrefix(mname, "API_") {
				apiName := mname[4:]
				if perm, err := parseApiPerm(apiName); err == nil && pmap[perm] {
					if api, ok := v.Method(i).Interface().(func(*Context) interface{}); ok {
						mod.apis[apiName] = api
					}
				}
			}
		}
	}
	mods[mod.Name] = mod
	modList = append(modList, mod)
}

type Mod struct {
	Obj      interface{} `json:"-"`
	Name     string
	Title    string
	TagTitle string
	Perms    []*Perm
	Order    int    `json:"-"`
	Parent   string `json:"-"`
	Icon     string
	apis     map[string]func(*Context) interface{}
}

type Perm struct {
	Name  string
	Title string
}

func CfgMod(name string, obj interface{}) *Mod {
	if mod, ok := cfgMods[name]; ok {
		mod.Obj = obj
		return mod
	}
	log.Fatal("mod not found in config: " + name)
	return nil
}

func parseApiPerm(api string) (string, error) {
	ss := strings.SplitN(api, "_", 2)
	if len(ss) != 2 {
		return "", errors.New("wrong format")
	}
	return ss[0], nil
}

func GetMods() []*Mod {
	list := []*Mod{}
	for _, row := range modList {
		m := &Mod{Name: row.Name, Title: row.Title, TagTitle: row.TagTitle, Perms: []*Perm{}, Order: row.Order, Parent: row.Parent, Icon: row.Icon}
		for _, p := range row.Perms {
			m.Perms = append(m.Perms, &Perm{Name: row.Name + "." + p.Name, Title: p.Title})
		}
		list = append(list, m)
	}
	return list
}

type ModTreeNode struct {
	Mod
	Children []*ModTreeNode
}

func (n *ModTreeNode) sort() {
	if n.Children == nil || len(n.Children) == 0 {
		return
	}
	sort.Slice(n.Children, func(i, j int) bool {
		return n.Children[i].Order > n.Children[j].Order
	})
	for _, c := range n.Children {
		c.sort()
	}
}

// iexprefix 屏蔽超级管理员某些权限
func GetModTree(uid int, iexprefix ...string) []*ModTreeNode {
	m := map[string]*ModTreeNode{}
	for _, row := range modList {
		n := &ModTreeNode{Mod: Mod{Name: row.Name, TagTitle: row.TagTitle, Title: row.Title, Perms: []*Perm{}, Order: row.Order, Parent: row.Parent, Icon: row.Icon}, Children: []*ModTreeNode{}}
		for _, p := range row.Perms {
			permName := row.Name + "." + p.Name
			if checkUserPerm(uid, permName) {
				if len(iexprefix) > 0 && isAdmin(uid) {
					patternd := false
					for _, prefix := range iexprefix {
						if strings.HasPrefix(permName, prefix) {
							patternd = true
							break
						}
					}
					if patternd {
						continue
					}
				}
				n.Perms = append(n.Perms, &Perm{Name: permName, Title: p.Title})
			}
		}
		m[n.Name] = n
	}
	tree := []*ModTreeNode{}
	for _, n := range m {
		if n.Parent == "" {
			tree = append(tree, n)
		} else {
			if p, ok := m[n.Parent]; ok {
				p.Children = append(p.Children, n)
			}
		}
	}
	sort.Slice(tree, func(i, j int) bool {
		return tree[i].Order > tree[j].Order
	})
	for _, n := range tree {
		n.sort()
	}
	return tree
}

type BatchModTree struct {
	Obj      interface{}
	Name     string
	Title    string
	TagTitle string
	Perms    []*Perm
	Order    int
	Children []*BatchModTree
	Icon     string
}

func (this *BatchModTree) deepRegMod(parent string) {
	modName := parent + "/" + this.Name
	if parent == "" {
		modName = this.Name
	}
	RegMod(&Mod{
		Obj:      this.Obj,
		Name:     modName,
		Title:    this.Title,
		TagTitle: this.TagTitle,
		Perms:    this.Perms,
		Order:    this.Order,
		Parent:   parent,
		Icon:     this.Icon,
	})
	if len(this.Children) > 0 {
		for _, mod := range this.Children {
			mod.deepRegMod(modName)
		}
	}
}

func BatchRegModTree(mods *BatchModTree) {
	mods.deepRegMod("")
}
